
#include "svdpi.h"
#include "common.h"
#include <queue>
#include <vector>
#include <string>

class FreeList
{
public:
    int wptr, rptr, size;
    std::vector<int> buffer;
    FreeList(int size) : size(size)
    {
        buffer.resize(size);
        clear();
    }
    ~FreeList() {}
    int entry_cnt;
    int allocate()
    {
        int value = buffer[rptr];
        rptr++;
        if (rptr == size)
            rptr = 0;
        entry_cnt--;
        return value;
    }
    void free(int entry)
    {
        buffer[wptr] = entry;
        wptr++;
        if (wptr == size)
            wptr = 0;
        entry_cnt++;
    }
    void redirect(int rptr)
    {
        int redirect_num = this->rptr - rptr;
        if (redirect_num < 0)
        {
            redirect_num += size;
        }
        // LOG("FreeList redirect_num : %d", redirect_num);
        entry_cnt += redirect_num;
        this->rptr = rptr;
    }
    bool empty()
    {
        return entry_cnt == 0;
    }
    bool full()
    {
        return entry_cnt == size;
    }
    void clear()
    {
        wptr = 0;
        rptr = 0;
        entry_cnt = 0;
    }
};

static std::vector<FreeList *> freelists;
static std::vector<std::string> freelist_names;
static int freelist_id = 0;

#ifdef __cplusplus
extern "C"
{
#endif
    void freelist_allocate(int id, int value)
    {
        if (id == 0)
            return;
        auto freelist = freelists[id - 1];
        if (freelist->empty())
        {
            LOG("Freelist[%d] %s allocate %d REF empty error at %ld", id, freelist_names[id - 1].c_str(), value, Verilated::time());
            Verilated::gotFinish(true);
        }
        int ref_value = freelist->allocate();
        if (ref_value != value)
        {
            LOG("Freelist[%d] %s allocate %d != ref %d error at %ld", id, freelist_names[id - 1].c_str(), value, ref_value, Verilated::time());
            Verilated::gotFinish(true);
        }
        return;
    }
    int freelist_create(int id, int size, const char *name)
    {
        if (id == 0)
        {
            freelists.push_back(new FreeList(size));
            freelist_names.push_back(std::string(name));
            freelist_id++;
            LOG("FreeList[%d] %s created", freelist_id, name);
            return freelist_id;
        }
        auto freelist = freelists[id - 1];
        freelist->clear();
        return id;
    }
    void freelist_free(int id, int value)
    {
        if (id == 0)
            return;
        auto freelist = freelists[id - 1];
        if (freelist->full())
        {
            LOG("Freelist[%d] %s free %d REF full error at %ld", id, freelist_names[id - 1].c_str(), value, Verilated::time());
            Verilated::gotFinish(true);
        }
        freelist->free(value);
    }
    void freelist_redirect(int id, int rptr)
    {
        if (id == 0)
            return;
        auto freelist = freelists[id - 1];
        freelist->redirect(rptr);
    }
#ifdef __cplusplus
}
#endif